/*
 *  +----------------------------------------------------------------------
 *  | sophon [ A FAST GAME FRAMEWORK ]
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023-2029 All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Licensed ( http:www.apache.org/licenses/LICENSE-2.0 )
 *  +----------------------------------------------------------------------
 *  | Author: jqiris <1920624985@qq.com>
 *  +----------------------------------------------------------------------
 */

use std::str::from_utf8;

use anyhow::{anyhow, Result};
use base64::{Engine as _, engine::general_purpose};
use openssl::pkey::{Private, Public};
use openssl::rsa::{Padding, Rsa};
use openssl::symm::{Cipher, decrypt, encrypt};
use prost::Message;
use serde::{de::DeserializeOwned, Serialize};

use crate::configs::EncryptConf;
use crate::prelude::{decode_json_msg, decode_proto_msg};
use crate::utils::read_all;

// 解密方式
pub enum DecryptType {
    DecryptTypeAes,
    DecryptTypeRsaPrikey,
    DecryptTypeRsaPubkey,
    DecryptTypeNone,
}

// 加密方式
pub enum EncryptType {
    EncryptTypeAes,
    EncryptTypeRsaPrikey,
    EncryptTypeRsaPubkey,
    EncryptTypeNone,
}

#[derive(Clone)]
pub struct Encipher {
    rsa_private_client: Rsa<Private>,
    rsa_public_client: Rsa<Public>,
    aes_client: Cipher,
    aes_key: String,
    aes_iv: String,
    unencrypt: bool,
}

impl Encipher {
    pub fn new(cfg: EncryptConf) -> Self {
        let (rsa_private_client, rsa_public_client) =
            Self::build_rsa_client(cfg.rsa_private_path.clone(), cfg.rsa_public_path.clone())
                .unwrap();
        Self {
            rsa_private_client,
            rsa_public_client,
            aes_client: Cipher::aes_128_cbc(),
            aes_key: cfg.aes_key.to_owned(),
            aes_iv: cfg.aes_iv.to_owned(),
            unencrypt: cfg.unencrypt,
        }
    }
    // 构建rsa客户端
    fn build_rsa_client(
        rsa_private_path: String,
        rsa_public_path: String,
    ) -> Result<(Rsa<Private>, Rsa<Public>)> {
        let rsa_private_key = read_all(rsa_private_path)?;
        let rsa_private = Rsa::private_key_from_pem(rsa_private_key.as_bytes())?;
        let rsa_public_key = read_all(rsa_public_path)?;
        let rsa_public = Rsa::public_key_from_pem(rsa_public_key.as_bytes())?;
        Ok((rsa_private, rsa_public))
    }
    //  rsa私钥加密
    pub fn rsa_private_encrypt(&self, data: &[u8]) -> Result<String> {
        let mut buf = vec![0; self.rsa_private_client.size() as usize];
        self.rsa_private_client
            .private_encrypt(data, &mut buf, Padding::PKCS1)?;
        // info!("rsa_private_encrypt bytes:{:?}",buf);
        Ok(general_purpose::STANDARD.encode(buf))
    }
    //  rsag公钥解密
    pub fn rsa_public_decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
        let ds = general_purpose::STANDARD.decode(data)?;
        let mut buf = vec![0; self.rsa_public_client.size() as usize];
        self.rsa_public_client
            .public_decrypt(&ds, &mut buf, Padding::PKCS1)?;
        Ok(buf)
    }
    //  rsa公钥加密
    pub fn rsa_public_encrypt(&self, data: &[u8]) -> Result<String> {
        let mut buf = vec![0; self.rsa_public_client.size() as usize];
        self.rsa_public_client
            .public_encrypt(data, &mut buf, Padding::PKCS1)?;
        // info!("rsa_public_encrypt bytes:{:?}",buf);
        Ok(general_purpose::STANDARD.encode(buf))
    }

    //  rsa私钥解密
    pub fn rsa_private_decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
        let ds = general_purpose::STANDARD.decode(data)?;
        let mut buf = vec![0; self.rsa_private_client.size() as usize];
        self.rsa_private_client
            .private_decrypt(&ds, &mut buf, Padding::PKCS1)?;
        Ok(buf)
    }
    // aes加密
    pub fn aes_encrypt(&self, data: &[u8]) -> Result<String> {
        let buf = encrypt(
            self.aes_client,
            self.aes_key.as_bytes(),
            Some(self.aes_iv.as_bytes()),
            data,
        )?;
        Ok(general_purpose::STANDARD.encode(buf))
    }
    //aes解密
    pub fn aes_decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
        let ds = general_purpose::STANDARD.decode(data)?;
        let buf = decrypt(
            self.aes_client,
            self.aes_key.as_bytes(),
            Some(self.aes_iv.as_bytes()),
            &ds,
        )?;
        Ok(buf)
    }
    //获取aes key iv
    pub fn aes_key_iv(&self) -> (String, String) {
        (self.aes_key.to_owned(), self.aes_iv.to_owned())
    }
}

//解码数据
pub fn decrypt_data<T: Serialize + DeserializeOwned>(
    encipher: &Encipher,
    mut decrypt_type: DecryptType,
    src: &[u8],
) -> Result<T> {
    if encipher.unencrypt {
        decrypt_type = DecryptType::DecryptTypeNone
    }
    let data = match decrypt_type {
        DecryptType::DecryptTypeAes => encipher.aes_decrypt(src),
        DecryptType::DecryptTypeRsaPrikey => encipher.rsa_private_decrypt(src),
        DecryptType::DecryptTypeRsaPubkey => encipher.rsa_public_decrypt(src),
        DecryptType::DecryptTypeNone => Ok(src.into()),
    }?;
    decode_json_msg(data)
}

pub fn decrypt_proto<T: Message + Default>(
    encipher: &Encipher,
    mut decrypt_type: DecryptType,
    src: &[u8],
) -> Result<T> {
    if encipher.unencrypt {
        decrypt_type = DecryptType::DecryptTypeNone
    }
    let data = match decrypt_type {
        DecryptType::DecryptTypeAes => encipher.aes_decrypt(src),
        DecryptType::DecryptTypeRsaPrikey => encipher.rsa_private_decrypt(src),
        DecryptType::DecryptTypeRsaPubkey => encipher.rsa_public_decrypt(src),
        DecryptType::DecryptTypeNone => Ok(src.into()),
    }?;
    decode_proto_msg(data)
}

//加密数据
pub fn encrypt_data(
    encipher: &Encipher,
    mut encrypt_type: EncryptType,
    src: &[u8],
) -> Result<String> {
    if encipher.unencrypt {
        encrypt_type = EncryptType::EncryptTypeNone
    }
    match encrypt_type {
        EncryptType::EncryptTypeAes => encipher.aes_encrypt(src),
        EncryptType::EncryptTypeRsaPrikey => encipher.rsa_private_encrypt(src),
        EncryptType::EncryptTypeRsaPubkey => encipher.rsa_public_encrypt(src),
        EncryptType::EncryptTypeNone => match from_utf8(src) {
            Ok(v) => Ok(v.to_owned()),
            Err(err) => Err(anyhow!(err.to_string())),
        },
    }
}
